Skip to content

Harden Druks UI and make it the scaffold default - #350

Merged
czpython merged 1 commit into
mainfrom
commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default
Aug 30, 2026
Merged

Harden Druks UI and make it the scaffold default#350
czpython merged 1 commit into
mainfrom
commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default

Conversation

@czpython

Copy link
Copy Markdown
Owner

ENG-906

Server-driven pages are now the path a new app takes by default, and the
surface is held to its own contract by tests that read the same wire snapshot
from both sides.

The scaffold

druks create app writes pages.py with a landing page, navigation naming
it, and no JavaScript: no Node, no React, no dist/. The generated AGENTS.md
states the page-purity contract and points at the UI contract, and the
generated suite renders the landing page. An app that needs full control of its
interface still ships an ESM frontend, which the author guide now describes as
the escape hatch it is — and says the author creates that dist/ themselves.

When a page fails

A page read that goes wrong answers PAGE_FAILED and names the app and the
page. What the app's own code said never reaches the browser — not its message
and not its type, either of which can carry a query, a URL, a credential, or
the name of a library. That stays in the process log. Druks says which of three
things happened: the page raised, it answered with something that is not a
Page, or it named an operation the app does not declare. The shell keeps all
three inside the app surface with a retry.

An Action exists only once a page function has run, so its reference is
checked when Druks builds the page — the earliest moment it exists. Everything
knowable earlier still fails the load: a repeated operation_id, one route
answering two methods, a page table no request could resolve, a navigation
entry that is not a static top-level page, a nested child, and a router on a
reserved segment.

Held to the contract

The proof app gained a catalog page carrying one of every block, value, and
field, and the exact wire it serializes is checked in as
frontend/src/druksui/catalog.json. The backend suite compares the live HTTP
response against that file and against the unions themselves, so a name added
to druks.ui with no example fails. The renderer's own suite renders that same
file. A field renamed on one side and not the other fails a suite instead of a
browser, and the backend workflow now runs when the renderer or the wire types
change.

That suite also holds every V1 renderer to alternative text, labelled inputs,
controls that are named and take focus, a spoken progress state, the chart's
numbers in a table, real column headers, and form-scoped control ids so two
forms sharing a field name keep their labels and radio groups apart.

How it is verified

Gates run: uv run ruff check backend, uv run ruff format --check backend,
npm --prefix frontend run lint, npm --prefix frontend test (209 tests
pass), npm --prefix frontend run build. uv run pytest backend/ did not run:
this machine has no Postgres or Redis. The scaffold, page-table, block, action,
and page-failure tests that need no database were run directly, and all pass.

Two limits worth naming. The accessibility suite runs in jsdom, so it holds
focus order, names, and roles, but a browser is still what would prove visible
focus, the dark theme, and a narrow viewport. And the proof app is the
standalone package fixture the contract tests read: this repository still ships
it, so a second one would only duplicate it.

@czpython
czpython force-pushed the commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default branch from a41a35a to 512d723 Compare August 29, 2026 22:55
@czpython
czpython force-pushed the commonzenpython/eng-905-add-forms-fields-and-operation-backed-actions branch 9 times, most recently from 9ef3f0c to 530b373 Compare August 30, 2026 12:18
Base automatically changed from commonzenpython/eng-905-add-forms-fields-and-operation-backed-actions to main August 30, 2026 12:21
@czpython
czpython force-pushed the commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default branch from 512d723 to f3ff32d Compare August 30, 2026 12:24
@mintlify

mintlify Bot commented Aug 30, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
druks 🟢 Ready View Preview Aug 30, 2026, 12:25 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@czpython
czpython force-pushed the commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default branch from f3ff32d to f94ce07 Compare August 30, 2026 12:28
@czpython
czpython force-pushed the commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default branch from f94ce07 to 1f3edfe Compare August 30, 2026 12:35
A scaffolded app now gets a landing page in Python, a navigation entry, and no
JavaScript: no Node, no React, no dist/. Its AGENTS.md states the page-purity
contract and points at the UI contract. An app that needs full control of its
interface still ships an ESM frontend, which the author guide describes as the
escape hatch it is.

A page that fails now says which page. A page function that raises, or a page
that names an operation the app does not declare, answers PAGE_FAILED with the
app and the page named, and so does a page whose shape its own contract cannot
carry. The traceback stays in the process log, and the shell keeps the failure
inside the app surface with a retry.

The proof app gained a catalog page carrying one of every block, value, and
field. backend/tests/test_ui_contract.py reads it through HTTP and compares
what it renders against the unions themselves, so a name added to druks.ui with
no example fails the suite. frontend/src/druksui/accessibility.test.tsx renders
the same catalog and holds every renderer to alternative text, labelled inputs,
named keyboard-reachable controls, a spoken progress state, the chart's own
table, and real column headers.

The author guide states what V1 leaves out and what is demand-pulled, and the
development guide and the overview point at the one canonical explanation.
@czpython
czpython force-pushed the commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default branch from 1f3edfe to b88e3ec Compare August 30, 2026 12:42
@czpython
czpython merged commit ee464a3 into main Aug 30, 2026
3 checks passed
@czpython
czpython deleted the commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default branch August 30, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant